import React, {useContext} from "react";
import {ThemeContext} from './theme-context';

/**
 * Context 提供了一个无需为每层组件手动添加 props，就能在组件树间进行数据传递的方法。
 *     1. 不必显式地通过组件树的逐层传递 props。使用 context, 我们可以避免通过中间元素传递 props。
 *     2. 它提升到组件树的更高层次来处理，会使得这些高层组件变得更复杂，并且会强行将低层组件适应这样的形式。
 *     3. 使用 context 的通用的场景包括管理当前的 locale，theme，或者一些缓存数据
 *
 * React.createContext  --- 对应 theme-context.js
 *     1. 创建一个 Context 对象。
 *     2. React 渲染一个订阅了这个 Context 对象的组件，这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。
 *     3. 只有当组件所处的树中没有匹配到 Provider 时，其 defaultValue 参数才会生效。
 *     4. 将 undefined 传递给 Provider 时，消费组件的 defaultValue 不会生效。
 *
 * Context.Provider  -- 对应 ContextApp.js 的 ThemeContext.Provider
 *     1. 每个 Context 对象都会返回一个 Provider React 组件，它允许消费组件订阅 context 的变化。
 *     2. Provider 接收一个 value 属性，传递给消费组件。
 *     3. 一个 Provider 可以和多个消费组件有对应关系。
 *     4. 多个 Provider 也可以嵌套使用，里层的会覆盖外层的数据。
 *     5. 当 Provider 的 value 值发生变化时，它内部的所有消费组件都会重新渲染。
 *     6. Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数，因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。
 *
 * Class.contextType  -- 函数式组件可用 useContext 取代
 *     1. 挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context 对象。
 *     2. 这能让你使用 this.context 来消费最近 Context 上的那个值。你可以在任何生命周期中访问到它，包括 render 函数中。
 *
 * Context.Consumer  -- 函数式组件可用 useContext 取代
 *     1. React 组件也可以订阅到 context 变更。
 *     2. 这能让你在函数式组件中完成订阅 context。
 *     3. 这个函数接收当前的 context 值，返回一个 React 节点。
 *     4. 传递给函数的 value 值等同于往上组件树离这个 context 最近的 Provider 提供的 value 值。
 *     5. 如果没有对应的 Provider，value 参数等同于传递给 createContext() 的 defaultValue。
 *
 * useContext 作用：
 *     1. 接收一个 context 对象（React.createContext 的返回值）并返回该 context 的当前值。
 *     2. 当组件上层最近的 <MyContext.Provider> 更新时，该 Hook 会触发重渲染，用最新的 context value 值。
 *     3. useContext(MyContext) 相当于 class 组件中的 static contextType = MyContext 或者 <MyContext.Consumer>。
 *     4. useContext(MyContext) 只是让你能够读取 context 的值以及订阅 context 的变化。
 */
export default function HookTestContext(props) {
    const theme = useContext(ThemeContext);

    return (
        <button
            {...props}
            style={{
                backgroundColor: theme.background,
                color: theme.foreground
            }}
        />
    );
}
